<?php

namespace app\common\library;

use alipay\aop\AopClient;
use alipay\aop\request\AlipayTradeAppPayRequest;
use app\common\controller\Api;
use app\common\model\events\Sign;
use app\common\model\MoneyLog;
use app\common\model\order\Order;
use app\common\model\recharge\Order as RechargeOrder;
use app\common\model\User;
use app\common\model\user\Commission;
use app\common\model\user\Info;
use think\Db;
use think\Env;
use think\Exception;
use think\Hook;
use think\Log;
use Throwable;

/**
 * 统一支付接口
 */
class Pay extends Api
{
    protected $noNeedLogin = ['*'];
    protected $noNeedRight = ['*'];
    protected $config;
    /**
     * 封装统一支付方法
     *
     */
    public static function pay($id, $userInfo, $pay_type, $attach)
    {

        Db::startTrans();
        try {
            $order = Order::get($id);
            if ($order->total_fee <= 0) {
                $pay_fee = '0.01';
            } else {
                $pay_fee = $order->total_fee;
            }
            //积分支付

            if (in_array(2, $pay_type)) {
                if ($pay_fee > 0 && $pay_fee > $userInfo->commission) {
                    $order->pay_point = $userInfo->commission;
                    $pay_fee = bcsub($pay_fee, $userInfo->money);
                } else {

                    $order->pay_point = $pay_fee;
                    $pay_fee = 0;
                    self::point($order);
                }
            }
            //消费券支付
            if (in_array(3, $pay_type)) {
                if ($pay_fee > 0 && $pay_fee > $userInfo->money) {
                    $order->pay_money = $userInfo->money;
                    $pay_fee = bcsub($pay_fee, $userInfo->money);
                } else {
                    $order->pay_money = $pay_fee;
                    $pay_fee = 0;
                    self::yue($order);
                }
            }

            //微信支付
            if (in_array(1, $pay_type)) {
                if ($pay_fee > 0) {
                    //微信小程序支付
                    $jsapi = Pay::wechatPay($order->sn, $order->pay_fee, $userInfo->openid);
                    $sdk = [
                        'timeStamp' => (string) $jsapi['timeStamp'],
                        'nonceStr' => $jsapi['nonceStr'],
                        'package' => $jsapi['package'],
                        'signType' => $jsapi['signType'],
                        'paySign' => $jsapi['paySign'],
                    ];
                }
            } else {
                if ($pay_fee > 0) {
                    exception('积分或消费券不足');
                }
            }
            if ($pay_fee > 0) {
                $order->pay_fee = $pay_fee;
            }
            $order->allowField(true)->save();
            Db::commit();
        } catch (\Exception $e) {
            Db::rollback();
            exception($e->getMessage());
        }
        return $sdk ?? '';
    }
    static function getOptions()
    {
        $options = new Config();
        $options->protocol = 'https';
        $options->gatewayHost = 'openapi.alipay.com';
        $options->signType = 'RSA2';
        $options->appId = Env::get('alipay.app_id');
        // 为避免私钥随源码泄露，推荐从文件中读取私钥字符串而不是写入源码中
        $options->merchantPrivateKey =  Env::get('alipay.private_key');
        // $options->alipayCertPath = config('site.alipay_public_key');
        // dd(ROOT_PATH . str_replace('/', DS, substr('/application/certs/alipayRootCert.crt', 1)));
        // $options->alipayRootCertPath = ROOT_PATH . str_replace('/', DS, substr('/application/certs/alipayRootCert.crt', 1));
        // $options->merchantCertPath = ROOT_PATH . str_replace('/', DS, substr('/application/certs/appCertPublicKey_2021004133682021.crt', 1));
        //注：如果采用非证书模式，则无需赋值上面的三个证书路径，改为赋值如下的支付宝公钥字符串即可
        $options->alipayPublicKey = Env::get('alipay.public_key');
        //可设置异步通知接收服务地址（可选）
        $options->notifyUrl = cdnurl('/api/notify/alipayNotify', true);
        return $options;
    }
    /**
     * 支付宝支付
     *
     */
    private static function alipay($sn, $pay_fee, $attach)
    {
        Factory::setOptions(self::getOptions());
        if (Env::get('app.debug') == true) {
            $pay_fee = 0.01;
        }
        try {
            //2. 发起API调用（以支付能力下的统一收单交易创建接口为例）
            $result = Factory::payment()->app()->optional('passback_params', $attach)->pay("订单支付", $sn, $pay_fee);
            $responseChecker = new ResponseChecker();
            if (!$responseChecker->success($result)) {
                exception("调用失败,原因：" . $result['msg'] . "," . $result['subMsg'] . PHP_EOL);
            }
        } catch (Exception $e) {
            exception("调用失败，" . $e->getMessage() . PHP_EOL);
        }
        return $result;
    }
    /**
     * 消费券支付
     *
     */
    private static function yue($data,  $check = 0, $payPwd = '')
    {

        $user = User::get($data['user_id']);

        // if ($user->pay_pwd != md5(md5($payPwd) . 'pcy')) {
        //     exception('支付密码不正确');
        // }

        if ($data->pay_money > $user->money) {
            exception('消费券不足');
        }
        Db::startTrans();
        try {
            if ($data->pay_money > 0) {
                User::money(-$data['pay_money'], $data['user_id'], '消费券支付商品');
            }
            if ($check <= 0) {
                self::orderFulfillment($data['sn']);
            }
            Db::commit();
        } catch (\Exception $e) {
            Db::rollback();
            exception($e->getMessage());
        }
        return true;
    }
    /**
     * 积分支付
     *
     */
    private static function point($data, $check = 0)
    {

        $user = User::get($data->user_id);

        if ($data->pay_point > $user->commission) {
            exception('积分不足');
        }
        Db::startTrans();
        try {
            if ($data->pay_point  > 0) {
                User::tocommission(-$data->pay_point, $data['user_id'], '积分支付商品');
            }
            if ($check == 0) {
                self::orderFulfillment($data['sn']);
            }
            Db::commit();
        } catch (\Exception $e) {
            Db::rollback();

            exception($e->getMessage());
        }
        return true;
    }


    //小程序&公众号支付
    public static function wechatPay($sn, $pay_fee, $openid, $attach = 'order')
    {
        $out_trade_no = $sn;
        $money = $pay_fee;

        if (empty($out_trade_no)) {
            exception('缺少参数order_sn');
        }
        // 官方提供网址
        $url = "https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi";
        $urlarr = parse_url($url); //拆解为：[scheme=>https,host=>api.mch.weixin.qq.com,path=>/v3/pay/transactions/native]
        $time = time(); //时间戳
        $noncestr = self::getNonceStr();
        $appid = Env::get('wechat.oauth_app_id'); //appID
        $mchid = Env::get('wechat.mchid'); //商户ID
        $xlid = Env::get('wechat.xlid'); //证书序列号

        $data['appid'] = $appid;
        $data['mchid'] = $mchid;
        $data['attach'] = $attach;
        $data['description'] = '商品描述'; //商品描述
        $data['out_trade_no'] = $out_trade_no; //订单编号，订单号在微信支付里是唯一的
        $data['notify_url'] = cdnurl('/api/notify/wechatNotify', true); //需根据自己的情况修改回调接口，也可以为空
        $data['amount']['total'] = round($money * 100, 0); //金额 单位 分
        $data['scene_info']['payer_client_ip'] = $_SERVER["REMOTE_ADDR"];; //场景ip
        $data['payer']['openid'] = $openid; //openid
        $array = json_encode($data, JSON_UNESCAPED_UNICODE);
        //签名，包含了$data数据、微信指定地址、随机数和时间
        $key = self::getSign($array, $urlarr['path'], $noncestr, $time);
        $token = sprintf('mchid="%s",serial_no="%s",nonce_str="%s",timestamp="%d",signature="%s"', $mchid, $xlid, $noncestr, $time, $key);
        //头部信息
        $header = array(
            'Accept: application/json',
            'Content-Type: application/json',
            'User-Agent:*/*',
            'Authorization: WECHATPAY2-SHA256-RSA2048 ' . $token
        );
        $res = self::curl_post_https($url, $array, $header);
        $res = json_decode($res, true);
        if (!isset($res['prepay_id'])) {
            exception($res['message']);
        }
        $prepay_id = $res['prepay_id'];
        $paySign = self::getWechartSign($appid, $time, $noncestr, 'prepay_id=' . $prepay_id);
        $payData = [
            'timeStamp' => (string) $time,
            'nonceStr' => $noncestr,
            'package' => 'prepay_id=' . $prepay_id,
            'signType' => 'RSA',
            'paySign' => $paySign
        ];
        return $payData;
    }

    // php -f D:\phpstudy_pro\WWW\1255\vendor\wechatpay\wechatpay\bin\CertificateDownloader.php -- -k QbyyrkXYWbwSVT3kw3FwBj1xv9b8rLLu -m 1586569011 -f C:\Users\Administrator\Desktop\1586569011_20230628_cert\apiclient_key.pem -s 63005C1EB8FE15AF1EA278F9C3F774D168929FCD -o C:\Users\Administrator\Desktop\1586569011_20230628_cert\
    // 0 1 * * *  /usr/bin/php /www/wwwroot/1214test.ciyuantiaoyue.com/1214-3.0/public/index.php /addons/crontab/autotask/index
    //微信app支付
    private static function wechatAppPay($order, $attach)
    {
        $out_trade_no = $order['sn'];
        $money = $order['pay_money'];
  
        if (empty($out_trade_no)) {
            exception('缺少参数order_sn');
        }
        // 官方提供网址
        $url = "https://api.mch.weixin.qq.com/v3/pay/transactions/app";
        $urlarr = parse_url($url); //拆解为：[scheme=>https,host=>api.mch.weixin.qq.com,path=>/v3/pay/transactions/native]
        $time = time(); //时间戳
        $noncestr = self::getNonceStr();
        $appid = config('site.app_id'); //appID
        $mchid = config('site.mch_id'); //商户ID
        $xlid = config('site.xlid'); //证书序列号
        $data = array();
        $data['appid'] = $appid;
        $data['mchid'] = $mchid;
        $data['attach'] = $attach;
        $data['description'] = '商品描述'; //商品描述
        $data['out_trade_no'] = $out_trade_no; //订单编号，订单号在微信支付里是唯一的
        $data['notify_url'] = cdnurl('/api/notify/wechatNotify', true); //需根据自己的情况修改回调接口，也可以为空
        $data['amount']['total'] = $money * 100; //金额 单位 分
        $data['scene_info']['payer_client_ip'] = $_SERVER["REMOTE_ADDR"];; //场景ip
        // $data['payer']['openid'] = $openid; //app支付不需要openid
        $data = json_encode($data, JSON_UNESCAPED_UNICODE);
        //签名，包含了$data数据、微信指定地址、随机数和时间
        $key = self::getSign($data, $urlarr['path'], $noncestr, $time);
        $token = sprintf('mchid="%s",serial_no="%s",nonce_str="%s",timestamp="%d",signature="%s"', $mchid, $xlid, $noncestr, $time, $key);
        //头部信息
        $header = array(
            'Accept: application/json',
            'Content-Type: application/json',
            'User-Agent:*/*',
            'Authorization: WECHATPAY2-SHA256-RSA2048 ' . $token
        );
        //向微信接口地址提交json格式的$data和header的头部信息，得到返回值
        $res = self::curl_post_https($url, $data, $header);
        $result = json_decode($res, true)['prepay_id'];
        if (!isset($result['prepay_id'])) {
            exception($result['message']);
        }
        $prepay_id = $result['prepay_id'];
        $paySign = self::getWechartSign($appid, $time, $noncestr, $prepay_id);
        $payData = [
            'appid' => $appid,
            'partnerid' => $mchid,
            'prepayid' => $prepay_id,
            'package' => "Sign=WXPay",
            'noncestr' => $noncestr,
            'timestamp' => (string) $time,
            'sign' => $paySign
        ];
        return $payData;
    }

    //订单支付成功
    public static function orderFulfillment($sn, $transaction_id = null)
    {
        $time = time();
        Db::startTrans();
        try {
            $order = Order::get(['sn' => $sn], ['goods']);

            if ($order) {
                if ($order->pay_money > 0) {
                    self::yue($order, 1);
                }
                if ($order->pay_point > 0) {
                    self::point($order, 1);
                }
                $order->order_status = '10';
                $order->paytime = $time;
                $order->transaction_id = $transaction_id;

                $order->save();

                // Hook::listen("order_pay_ok", $order);
                $a = Commission::orderPayOk($order);
            }
            Db::commit();
        } catch (\Exception | Exception | Throwable $e) {
            Db::rollback();
            Log::error('支付异常' . var_export($e, 1));
            exception($e->getMessage());
        }
        // Hook::listen("order_pay_ok", $order);
    }

    //获取随机字符串
    public static function getNonceStr()
    {
        $strs = "QWERTYUIOPASDFGHJKLZXCVBNM1234567890";
        $name = substr(str_shuffle($strs), mt_rand(0, strlen($strs) - 11), 32);
        return $name;
    }

    //微信支付签名
    public static function getSign($data, $url, $randstr = '', $time = '')
    {
        $str = "POST" . "\n" . $url . "\n" . $time . "\n" . $randstr . "\n" . $data . "\n";
        $key = file_get_contents(ROOT_PATH . str_replace('/', DS, substr('/application/certs/apiclient_key.pem', 1))); //在商户平台下载的秘钥,读取到变量
        $str = self::getSha256WithRSA($str, $key);
        return $str;
    }

    //调起支付的签名
    public static function getWechartSign($appid, $timeStamp, $noncestr, $prepay_id)
    {
        $str = $appid . "\n" . $timeStamp . "\n" . $noncestr . "\n" . $prepay_id . "\n";
        $key = file_get_contents(ROOT_PATH . str_replace('/', DS, substr('/application/certs/apiclient_key.pem', 1))); //在商户平台下载的秘钥,读取到变量
        $str = self::getSha256WithRSA($str, $key);
        return $str;
    }

    //加密
    public static function getSha256WithRSA($content, $privateKey)
    {
        $binary_signature = "";
        $algo = "SHA256";
        openssl_sign($content, $binary_signature, $privateKey, $algo);
        $sign = base64_encode($binary_signature);
        return $sign;
    }

    /* PHP CURL HTTPS POST */
    public static function curl_post_https($url, $data, $header)
    { // 模拟提交数据函数
        $curl = curl_init(); // 启动一个CURL会话
        curl_setopt($curl, CURLOPT_URL, $url); // 要访问的地址
        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0); // 对认证证书来源的检查
        curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 0); // 从证书中检查SSL加密算法是否存在，如果出错则修改为0，默认为1
        curl_setopt($curl, CURLOPT_USERAGENT, $_SERVER['HTTP_USER_AGENT']); // 模拟用户使用的浏览器
        curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1); // 使用自动跳转
        curl_setopt($curl, CURLOPT_AUTOREFERER, 1); // 自动设置Referer
        curl_setopt($curl, CURLOPT_POST, 1); // 发送一个常规的Post请求
        curl_setopt($curl, CURLOPT_POSTFIELDS, $data); // Post提交的数据包
        curl_setopt($curl, CURLOPT_TIMEOUT, 30); // 设置超时限制防止死循环
        curl_setopt($curl, CURLOPT_HEADER, 0); // 显示返回的Header区域内容
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); // 获取的信息以文件流的形式返回
        curl_setopt($curl, CURLOPT_HTTPHEADER, $header);
        $tmpInfo = curl_exec($curl); // 执行操作
        if (curl_errno($curl)) {
            echo 'Errno' . curl_error($curl); //捕抓异常
        }
        curl_close($curl); // 关闭CURL会话
        return $tmpInfo; // 返回数据，json格式
    }

      //退款
      public static function refund($refund_fee, $total_fee, $out_refund_no, $out_trade_no)
      {
  
          //商户订单号,微信生成的退款订单号 二选一即可
  
          if (Env::get('app.debug') == true) {
              $refund_fee = 0.01;
          }
          $time = time();
          $refundData = [
              'out_refund_no' => $out_refund_no, //退款编号
              'reason' => '商品退款',
              'notify_url' => cdnurl('/api/notify/refundNotify', true),
              'funds_account' => 'AVAILABLE',
              'amount' => [
                  'refund' => (float) bcmul($refund_fee, 100, 0), //退款标价金额，单位为分，可以做部分退款
                  'total' => (float) bcmul($total_fee, 100, 0), //订单总金额，单位为分
                  'currency' => 'CNY'
              ]
          ];
          $refundData['out_trade_no'] = $out_trade_no;
          $url = 'https://api.mch.weixin.qq.com/v3/refund/domestic/refunds';
          $urlarr = parse_url($url); //拆解为：[scheme=>https,host=>api.mch.weixin.qq.com,path=>/v3/pay/transactions/native]
          $mchid = Env::get('wechat.mchid'); //商户ID
          $xlid = Env::get('wechat.xlid'); //证书序列号
          $refundData = json_encode($refundData);
          $nonce = self::getNonceStr();
          $key = self::getSign($refundData, $urlarr['path'], $nonce, $time);
          $token = sprintf('mchid="%s",serial_no="%s",nonce_str="%s",timestamp="%d",signature="%s"', $mchid, $xlid, $nonce, $time, $key);
          $header = array(
              'Accept: application/json',
              'Content-Type: application/json',
              'User-Agent:*/*',
              'Authorization: WECHATPAY2-SHA256-RSA2048 ' . $token
          );
  
          $res = self::curl_post_https($url, $refundData, $header);
          $res_array = json_decode($res, true);
          if (isset($res_array['status']) && ($res_array['status'] == 'PROCESSING' || $res_array['status'] == 'SUCCESS') && isset($res_array['status'])) {
          } else {
              exception($res_array['message']);
          }
      }
}
